/**
 * WMS打印服务客户端
 * 用于与独立的Electron桌面打印应用通信
 */
// 默认像素/毫米比例
const DEFAULT_PX_PER_MM = 3.78;

// 拦截器相关变量
let originalXHR = null;
let isInterceptorActive = false;

/**
 * 设置XMLHttpRequest错误拦截器
 * 注意：此函数现在不再设置全局拦截器，而是返回特定于打印服务的XHR实例
 * @returns {XMLHttpRequest} 打印服务专用XMLHttpRequest实例
 */
function getInterceptedXHR() {
  if (typeof window === 'undefined' || !window.XMLHttpRequest) return null;
  
  // 保存原始的XMLHttpRequest，如果尚未保存
  if (!originalXHR) {
    originalXHR = window.XMLHttpRequest;
  }
  
  // 创建一个拦截XHR的实例
  const xhr = new originalXHR();
  
  // 保存原始的open方法
  const originalOpen = xhr.open;
  xhr.open = function(...args) {
    try {
      const result = originalOpen.apply(this, args);
      // 不再自动设置responseType为空，让调用者控制responseType
      return result;
    } catch (error) {
      console.error('WMS打印服务: XMLHttpRequest open 错误:', error);
      throw error;
    }
  };
  
  // 不再拦截responseText访问，避免与其他responseType冲突
  
  return xhr;
}

/**
 * 恢复原始XMLHttpRequest
 * (在当前实现中不再需要此功能，因为我们不再全局替换XMLHttpRequest)
 */
function restoreXHR() {
  if (isInterceptorActive && originalXHR && typeof window !== 'undefined') {
    window.XMLHttpRequest = originalXHR;
    isInterceptorActive = false;
    console.log('WMS打印服务: 已恢复原始XMLHttpRequest');
  }
}

/**
 * WebSocket客户端类
 */
class WebSocketClient {
  /**
   * 构造函数
   * @param {string} url - WebSocket服务器地址
   */
  constructor(url = 'ws://localhost:20000') {
    this.url = url;
    this.socket = null;
    this.connected = false;
    this.connecting = false;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 5;
    this.reconnectInterval = 3000; // 3秒
    this.callbacks = {
      onConnect: null,
      onDisconnect: null,
      onError: null,
      onMessage: null
    };
    this.debug = false;
    this.pendingRequests = new Map();
    this.requestTimeout = 30000; // 30秒
    
    // 不再自动设置全局XMLHttpRequest拦截器
  }

  /**
   * 为打印服务获取一个特定的XMLHttpRequest实例
   * @returns {XMLHttpRequest} 拦截处理后的XMLHttpRequest实例
   */
  getCustomXHR() {
    try {
      return getInterceptedXHR();
    } catch (error) {
      this._log('获取自定义XMLHttpRequest实例失败:', error);
      // 失败时返回原始的XMLHttpRequest
      return new (originalXHR || window.XMLHttpRequest)();
    }
  }

  /**
   * 发送AJAX请求(用于WebSocketClient与打印服务的通信)
   * @param {Object} options - 请求选项
   * @returns {Promise<any>} 响应结果
   */
  sendAjaxRequest(options) {
    return new Promise((resolve, reject) => {
      const xhr = this.getCustomXHR();
      if (!xhr) {
        reject(new Error('无法创建XHR实例'));
        return;
      }

      xhr.open(options.method || 'GET', options.url, true);
      
      // 设置请求头
      if (options.headers) {
        Object.keys(options.headers).forEach(key => {
          xhr.setRequestHeader(key, options.headers[key]);
        });
      }
      
      // 设置超时
      xhr.timeout = options.timeout || 30000;
      
      // 设置responseType(如果提供)
      if (options.responseType) {
        try {
          xhr.responseType = options.responseType;
        } catch (error) {
          console.warn(`无法设置responseType='${options.responseType}':`, error);
        }
      }
      
      // 事件处理
      xhr.onload = () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          try {
            let response;
            
            // 根据responseType处理响应
            if (xhr.responseType === 'json') {
              response = xhr.response;
            } else if (xhr.responseType === '' || xhr.responseType === 'text') {
              // 尝试解析JSON，如果失败则返回原始文本
              try {
                response = JSON.parse(xhr.responseText);
              } catch (e) {
                response = xhr.responseText;
              }
            } else if (xhr.responseType === 'blob' || xhr.responseType === 'arraybuffer') {
              // 直接返回响应，不尝试解析
              response = xhr.response;
            } else {
              // 其他类型直接返回
              response = xhr.response;
            }
            
            resolve(response);
          } catch (error) {
            this._log('解析响应失败:', error);
            reject(new Error(`解析响应失败: ${error.message}`));
          }
        } else {
          reject(new Error(`请求失败: ${xhr.status} ${xhr.statusText}`));
        }
      };
      
      xhr.onerror = () => {
        reject(new Error('网络请求错误'));
      };
      
      xhr.ontimeout = () => {
        reject(new Error('请求超时'));
      };
      
      // 发送请求
      xhr.send(options.data || null);
    });
  }

  /**
   * 设置回调函数
   * @param {string} event - 事件名称
   * @param {Function} callback - 回调函数
   */
  on(event, callback) {
    if (this.callbacks.hasOwnProperty(event)) {
      this.callbacks[event] = callback;
      return true;
    }
    return false;
  }

  /**
   * 连接到WebSocket服务器
   * @returns {Promise<Object>} 连接结果
   */
  connect() {
    if (this.connected) {
      return Promise.resolve({ success: true, message: '已连接' });
    }

    if (this.connecting) {
      return new Promise((resolve, reject) => {
        const checkInterval = setInterval(() => {
          if (!this.connecting) {
            clearInterval(checkInterval);
            if (this.connected) {
              resolve({ success: true, message: '已连接' });
            } else {
              reject(new Error('连接失败'));
            }
          }
        }, 100);

        setTimeout(() => {
          clearInterval(checkInterval);
          if (this.connecting) {
            this.connecting = false;
            reject(new Error('连接超时'));
          }
        }, 5000);
      });
    }

    this.connecting = true;
    return new Promise((resolve, reject) => {
      try {
        this._log('正在连接到打印服务:', this.url);
        this.socket = new WebSocket(this.url);

        // 设置WebSocket可以接收二进制数据，但我们会将其转换为文本
        this.socket.binaryType = 'arraybuffer';

        this.socket.onopen = () => {
          this._log('连接成功');
          this.connected = true;
          this.connecting = false;
          this.reconnectAttempts = 0;

          if (this.callbacks.onConnect) {
            this.callbacks.onConnect();
          }

          resolve({ success: true, message: '连接成功' });
        };

        this.socket.onclose = (event) => {
          this._log('连接关闭:', event.code, event.reason);
          this.connected = false;
          this.connecting = false;

          // 清除所有挂起的请求
          this._clearPendingRequests('连接已关闭');

          if (this.callbacks.onDisconnect) {
            this.callbacks.onDisconnect(event);
          }

          // 尝试重新连接
          this._attemptReconnect();
        };

        this.socket.onerror = (error) => {
          this._log('连接错误:', error);
          this.connecting = false;

          if (this.callbacks.onError) {
            this.callbacks.onError(error);
          }

          reject(new Error('连接错误'));
        };

        this.socket.onmessage = (event) => {
          this._handleMessage(event);
        };
      } catch (error) {
        this._log('创建WebSocket连接失败:', error);
        this.connecting = false;
        reject(error);
      }
    });
  }

  /**
   * 断开连接
   */
  disconnect() {
    if (this.socket) {
      this.socket.close();
      this.socket = null;
      this.connected = false;
      this.connecting = false;
      this._log('已断开连接');
    }
  }

  /**
   * 发送消息到服务器
   * @param {Object} data - 要发送的数据
   * @returns {Promise<Object>} 响应结果
   */
  send(data) {
    return new Promise((resolve, reject) => {
      if (!this.connected) {
        reject(new Error('未连接到服务器'));
        return;
      }

      try {
        // 添加请求ID用于匹配响应
        const requestId = this._generateRequestId();
        const message = {
          ...data,
          requestId,
          // 明确告知服务器使用文本响应
          preferTextResponse: true
        };

        // 提取特殊的XHR配置参数
        delete message._responseType;

        // 添加到挂起请求
        this._addPendingRequest(requestId, resolve, reject);

        // 发送消息
        this.socket.send(JSON.stringify(message));
        this._log('已发送消息:', message);
      } catch (error) {
        this._log('发送消息失败:', error);
        reject(error);
      }
    });
  }

  /**
   * 处理接收到的消息
   * @private
   * @param {MessageEvent} event - 消息事件
   */
  _handleMessage(event) {
    try {
      let response;
      
      // 处理不同类型的消息数据
      if (typeof event.data === 'string') {
        // 文本消息，直接解析
        try {
          response = JSON.parse(event.data);
        } catch (parseError) {
          this._log('JSON解析失败:', parseError);
          this._log('原始数据:', event.data);
          throw new Error('消息格式无效: 不是有效的JSON');
        }
      } else if (event.data instanceof ArrayBuffer) {
        // 二进制数据处理
        try {
          // 使用TextDecoder安全地转换ArrayBuffer到字符串
          const textDecoder = new TextDecoder('utf-8');
          const jsonText = textDecoder.decode(event.data);
          
          try {
            // 尝试将文本解析为JSON
            response = JSON.parse(jsonText);
            this._log('成功从ArrayBuffer解析JSON数据');
          } catch (jsonError) {
            // 如果不是JSON，保留原始文本
            this._log('ArrayBuffer内容不是有效的JSON，将使用原始文本');
            response = { 
              raw: jsonText,
              type: 'text',
              fromArrayBuffer: true
            };
          }
        } catch (bufferError) {
          this._log('ArrayBuffer解析失败:', bufferError);
          // 创建一个有意义的响应对象，而不是抛出错误
          response = {
            error: '无法解析二进制消息',
            errorDetails: bufferError.message,
            type: 'error',
            fromArrayBuffer: true
          };
        }
      } else if (event.data instanceof Blob) {
        // Blob数据处理
        this._log('接收到Blob数据，尝试读取');
        const reader = new FileReader();
        reader.onload = () => {
          try {
            const jsonText = reader.result;
            let blobResponse;
            
            try {
              // 尝试解析为JSON
              blobResponse = JSON.parse(jsonText);
            } catch (jsonError) {
              // 如果不是JSON，创建一个包装响应
              this._log('Blob内容不是有效的JSON格式');
              blobResponse = {
                raw: jsonText,
                type: 'text',
                fromBlob: true
              };
            }
            
            // 触发消息回调
            if (this.callbacks.onMessage) {
              this.callbacks.onMessage(blobResponse);
            }
            
            // 处理挂起的请求
            this._processResponse(blobResponse);
          } catch (blobError) {
            this._log('Blob解析失败:', blobError);
            
            // 即使解析失败，也创建一个有意义的响应
            const errorResponse = {
              error: '无法解析Blob数据',
              errorDetails: blobError.message,
              type: 'error',
              fromBlob: true
            };
            
            if (this.callbacks.onMessage) {
              this.callbacks.onMessage(errorResponse);
            }
            
            this._processResponse(errorResponse);
          }
        };
        reader.onerror = (readError) => {
          this._log('读取Blob失败:', readError);
          
          // 发送错误响应
          const errorResponse = {
            error: '读取Blob数据失败',
            errorDetails: readError.message || '未知错误',
            type: 'error',
            fromBlob: true
          };
          
          if (this.callbacks.onMessage) {
            this.callbacks.onMessage(errorResponse);
          }
          
          this._processResponse(errorResponse);
        };
        reader.readAsText(event.data);
        
        // 因为Blob读取是异步的，这里直接返回
        return;
      } else {
        this._log('收到未知类型的消息:', typeof event.data);
        response = {
          error: `不支持的消息类型: ${typeof event.data}`,
          type: 'error',
          unknownType: true
        };
      }
      
      this._log('收到消息:', response);

      // 触发通用消息回调
      if (this.callbacks.onMessage) {
        this.callbacks.onMessage(response);
      }

      // 处理消息响应
      this._processResponse(response);
    } catch (error) {
      this._log('处理消息失败:', error);
      
      // 发送一个错误响应以避免挂起的请求永远不被解决
      const errorResponse = {
        error: '处理消息时出错',
        errorDetails: error.message,
        type: 'error'
      };
      
      if (this.callbacks.onMessage) {
        this.callbacks.onMessage(errorResponse);
      }
      
      this._processResponse(errorResponse);
    }
  }
  
  /**
   * 处理响应消息
   * @private
   * @param {Object} response - 响应对象
   */
  _processResponse(response) {
    // 如果响应是错误类型，但没有requestId，我们需要处理所有挂起的请求
    if (response.type === 'error' && !response.requestId && this.pendingRequests.size > 0) {
      this._log('收到未关联到特定请求的错误响应，处理所有挂起请求');
      // 解决所有挂起的请求为错误
      for (const [requestId, { reject }] of this.pendingRequests.entries()) {
        this._removePendingRequest(requestId);
        reject(new Error(response.error || '未知错误'));
      }
      return;
    }

    // 检查是否有匹配的挂起请求
    if (response.requestId && this.pendingRequests.has(response.requestId)) {
      const { resolve, reject } = this.pendingRequests.get(response.requestId);
      this._removePendingRequest(response.requestId);

      // 检查响应中是否有错误
      if (response.error || (response.type === 'error')) {
        const errorMessage = response.error || response.errorDetails || '未知错误';
        this._log(`请求 ${response.requestId} 返回错误:`, errorMessage);
        reject(new Error(errorMessage));
      } else {
        // 成功响应处理
        this._log(`请求 ${response.requestId} 成功完成`);
        resolve(response);
      }
    } else if (response.requestId) {
      // 有requestId但没有匹配的挂起请求
      this._log(`收到未知请求ID的响应: ${response.requestId}`);
    } else {
      // 没有requestId的响应，可能是服务器主动推送的消息
      this._log('收到无requestId的消息，可能是服务器推送');
    }
  }

  /**
   * 尝试重新连接
   * @private
   */
  _attemptReconnect() {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      this._log('达到最大重连次数，不再尝试重连');
      return;
    }

    this.reconnectAttempts++;
    const delay = Math.min(
      this.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1),
      30000 // 最大30秒
    );

    this._log(`计划在${delay}ms后第${this.reconnectAttempts}次重连`);

    setTimeout(() => {
      if (!this.connected && !this.connecting) {
        this._log(`尝试第${this.reconnectAttempts}次重连`);
        this.connect().catch(error => {
          this._log('重连失败:', error);
        });
      }
    }, delay);
  }

  /**
   * 生成请求ID
   * @private
   * @returns {string} 唯一请求ID
   */
  _generateRequestId() {
    return Date.now().toString(36) + Math.random().toString(36).substring(2, 9);
  }

  /**
   * 添加挂起请求
   * @private
   * @param {string} requestId - 请求ID
   * @param {Function} resolve - 成功回调
   * @param {Function} reject - 失败回调
   */
  _addPendingRequest(requestId, resolve, reject) {
    const timeoutId = setTimeout(() => {
      if (this.pendingRequests.has(requestId)) {
        const { reject } = this.pendingRequests.get(requestId);
        this._removePendingRequest(requestId);
        reject(new Error('请求超时'));
      }
    }, this.requestTimeout);

    this.pendingRequests.set(requestId, { resolve, reject, timeoutId });
  }

  /**
   * 移除挂起请求
   * @private
   * @param {string} requestId - 请求ID
   */
  _removePendingRequest(requestId) {
    if (this.pendingRequests.has(requestId)) {
      const { timeoutId } = this.pendingRequests.get(requestId);
      clearTimeout(timeoutId);
      this.pendingRequests.delete(requestId);
    }
  }

  /**
   * 清除所有挂起请求
   * @private
   * @param {string} reason - 清除原因
   */
  _clearPendingRequests(reason) {
    for (const [requestId, { reject, timeoutId }] of this.pendingRequests.entries()) {
      clearTimeout(timeoutId);
      reject(new Error(reason));
    }
    this.pendingRequests.clear();
  }

  /**
   * 打印日志
   * @private
   * @param  {...any} args - 日志参数
   */
  _log(...args) {
    if (this.debug) {
      console.log('[WebSocketClient]', ...args);
    }
  }
}

/**
 * WMS打印服务类
 */
class WMSPrinter {
  /**
   * 构造函数
   * @param {Object} options - 配置选项
   */
  constructor(options = {}) {
    // 合并默认选项
    this.options = {
      serverUrl: 'ws://localhost:20000',
      autoConnect: false, // 默认不自动连接，改为由应用程序明确控制
      reconnect: true,
      maxReconnectAttempts: 5,
      reconnectInterval: 3000,
      defaultPrinter: '',
      debug: false,
      ...options
    };

    // 从localStorage加载自定义URL（如果有）
    try {
      const savedUrl = localStorage.getItem('wmsPrinterUrl');
      if (savedUrl) {
        this.options.serverUrl = savedUrl;
        this._log(`从本地存储加载连接地址: ${savedUrl}`);
      }
    } catch (error) {
      this._log('从本地存储加载连接地址失败:', error);
    }

    // 创建WebSocket客户端
    this.client = new WebSocketClient(this.options.serverUrl);
    this.client.debug = this.options.debug;

    // 设置事件回调
    this.client.on('onConnect', () => this._onConnect());
    this.client.on('onDisconnect', () => this._onDisconnect());
    this.client.on('onError', (error) => this._onError(error));

    this.printers = [];
    this.connected = false;
    this.fallbackHandler = null;

    // 尝试从localStorage读取默认打印机
    this._loadDefaultPrinter();

    // 如果配置为自动连接，则连接服务器
    if (this.options.autoConnect) {
      this.connect();
    }
  }

  /**
   * 获取打印服务专用的XMLHttpRequest实例
   * @returns {XMLHttpRequest} 专用XMLHttpRequest实例
   */
  getServiceXHR() {
    return this.client.getCustomXHR();
  }

  /**
   * 发送HTTP请求（使用打印服务专用的XHR实例）
   * @param {Object} options - 请求选项
   * @returns {Promise<any>} 响应结果
   */
  sendHttpRequest(options) {
    return this.client.sendAjaxRequest(options);
  }

  /**
   * 下载文件（使用打印服务专用的XHR实例）
   * @param {string} url - 文件URL
   * @param {string} filename - 保存的文件名
   * @returns {Promise<Blob>} 文件Blob对象
   */
  async downloadFile(url, filename) {
    try {
      this._log(`开始下载文件: ${url}`);
      
      const xhr = this.getServiceXHR();
      
      return new Promise((resolve, reject) => {
        xhr.open('GET', url, true);
        
        try {
          // 明确设置responseType为blob
          xhr.responseType = 'blob';
        } catch (error) {
          this._log('设置responseType=blob失败:', error);
          // 如果设置失败，不要抛出错误，继续尝试下载
        }
        
        xhr.onload = () => {
          if (xhr.status === 200) {
            try {
              // 获取响应，可能是blob或其他类型
              const response = xhr.response;
              let blob;
              
              if (response instanceof Blob) {
                blob = response;
              } else {
                // 如果响应不是Blob，尝试创建一个Blob
                this._log('响应不是Blob，尝试转换');
                try {
                  if (typeof response === 'string') {
                    blob = new Blob([response], { type: 'application/octet-stream' });
                  } else if (response instanceof ArrayBuffer) {
                    blob = new Blob([response], { type: 'application/octet-stream' });
                  } else {
                    throw new Error('无法将响应转换为Blob');
                  }
                } catch (e) {
                  reject(new Error(`转换响应为Blob失败: ${e.message}`));
                  return;
                }
              }
              
              // 如果需要自动下载
              if (filename) {
                const a = document.createElement('a');
                const url = URL.createObjectURL(blob);
                a.href = url;
                a.download = filename;
                document.body.appendChild(a);
                a.click();
                setTimeout(() => {
                  document.body.removeChild(a);
                  URL.revokeObjectURL(url);
                }, 0);
              }
              
              resolve(blob);
            } catch (error) {
              reject(new Error(`处理响应失败: ${error.message}`));
            }
          } else {
            reject(new Error(`下载失败: ${xhr.status} ${xhr.statusText}`));
          }
        };
        
        xhr.onerror = () => {
          reject(new Error('网络错误'));
        };
        
        xhr.send();
      });
    } catch (error) {
      this._log('下载文件失败:', error);
      throw error;
    }
  }

  /**
   * 连接到打印服务
   * @returns {Promise<Object>} 连接结果
   */
  async connect() {
    try {
      const result = await this.client.connect();
      this.connected = true;
      return result;
    } catch (error) {
      this._log('连接失败:', error);
      this.connected = false;
      throw error;
    }
  }

  /**
   * 断开连接
   */
  disconnect() {
    this.client.disconnect();
    this.connected = false;
  }

  /**
   * 获取打印机列表
   * @returns {Promise<Array>} 打印机列表
   */
  async getPrinters() {
    try {
      // 如果没有连接，先连接
      if (!this.connected) {
        await this.connect();
      }

      const response = await this.client.send({
        action: 'getPrinters'
      });
      console.log('获取打印机列表响应:', response);
      if (response && response.printers) {
        this.printers = response.printers;
        return this.printers;
      }

      throw new Error('获取打印机列表失败: 无效的响应');
    } catch (error) {
      this._log('获取打印机列表失败:', error);

      // 如果是连接错误，尝试使用降级处理
      if (this.fallbackHandler) {
        return [];
      }

      throw error;
    }
  }

  /**
   * 打印PDF文件
   * @param {Blob} pdfBlob - PDF文件Blob
   * @param {string} printerName - 打印机名称
   * @param {Object} options - 打印选项
   * @returns {Promise<Object>} 打印结果
   */
  async printPdf(pdfBlob, printerName, options = {}) {
    try {
      // 如果没有连接，先连接
      // if (!this.connected) {
      //   console.log("没有连接，先连接")
      //   await this.connect();
      // }

      // 如果没有指定打印机，使用默认打印机
      const targetPrinter = printerName || this.options.defaultPrinter;
      if (!targetPrinter) {
        throw new Error('未指定打印机且无默认打印机');
      }

      // 检查PDF Blob
      if (!(pdfBlob instanceof Blob)) {
        throw new Error('无效的PDF数据：不是Blob对象');
      }
      
      if (pdfBlob.size === 0) {
        throw new Error('PDF数据大小为0');
      }
      
      // 如果Blob类型不是PDF，重新创建一个正确类型的Blob
      let verifiedBlob = pdfBlob;
      if (pdfBlob.type !== 'application/pdf') {
        this._log('修正PDF MIME类型为application/pdf，原类型:', pdfBlob.type);
        verifiedBlob = new Blob([pdfBlob], { type: 'application/pdf' });
      }

      // 将PDF转换为Base64
      try {
        this._log('开始将PDF转换为Base64...');
        var base64Data = await this._blobToBase64(verifiedBlob);
        this._log('PDF成功转换为Base64，数据长度:', base64Data.length);
      } catch (blobError) {
        this._log('PDF转换为Base64失败:', blobError);
        throw new Error(`PDF转换失败: ${blobError.message}`);
      }

      // 合并打印选项
      const printOptions = {
        printer: targetPrinter,
        silent: options.silent !== undefined ? options.silent : true,
        copies: options.copies || 1,
        printBackground: options.printBackground !== undefined ? options.printBackground : true,
        ...options
      };

      // 发送打印请求
      this._log('发送打印请求:', {
        printer: targetPrinter,
        options: printOptions,
        dataSize: base64Data.length
      });
      
      try {
        const response = await this.client.send({
          action: 'printPdf',
          data: base64Data,
          options: printOptions,
          // 确保不使用二进制传输
          preferTextResponse: true
        });

        if (response && response.success) {
          this._log('打印请求成功处理');
          return response;
        }

        throw new Error(response.error || '打印失败: 未知错误');
      } catch (sendError) {
        this._log('发送打印请求失败:', sendError);
        throw new Error(`发送打印请求失败: ${sendError.message}`);
      }
    } catch (error) {
      this._log('打印PDF失败:', error);

      // 如果是连接错误，尝试使用降级处理
      if (this.fallbackHandler) {
        this._log('使用降级打印方式');
        return this.fallbackHandler(pdfBlob, printerName, options);
      }

      throw error;
    }
  }

  /**
   * 打印图片
   * @param {string} base64Data - base64编码的图片数据
   * @param {string} printerName - 打印机名称
   * @param {Object} options - 打印选项
   * @returns {Promise<Object>} - 返回包含成功状态和消息的Promise
   */
  printImage(base64Data, printerName, options = {}) {
    // 参数验证
    if (!base64Data || typeof base64Data !== 'string') {
      return Promise.reject(new Error('图片数据不能为空且必须是字符串'));
    }

    if (!printerName || typeof printerName !== 'string') {
      return Promise.reject(new Error('打印机名称不能为空'));
    }

    this._log('开始图片打印', { printer: printerName });

    // 检查打印服务是否已连接
    if (!this.connected) {
      this._log('打印服务未连接，尝试连接');
      
      return this.connect().then(() => {
        return this.printImage(base64Data, printerName, options);
      }).catch((error) => {
        this._log('连接打印服务失败', error);
        
        // 使用降级处理
        if (this.fallbackHandler) {
          this._log('使用降级处理打印图片');
          return this.fallbackHandler(base64Data, printerName, options, 'image');
        }
        
        return Promise.reject(new Error('打印服务连接失败，且未设置降级处理函数'));
      });
    }
    
    try {
      // 发送打印请求
      this._log('发送图片打印请求', {
        printer: printerName,
        options: options,
        dataSize: base64Data.length
      });
      console.log('发送图片打印请求', {
        printer: printerName,
        options: options,
        dataSize: base64Data.length
      });
      return this.client.send({
        action: options.action?options.action:'printImage',
        data: base64Data,
        options: {
          printer: printerName,
          silent: options.silent !== undefined ? options.silent : true,
          copies: options.copies || 1,
          ...options,
          
        },
        preferTextResponse: true
      }).then(response => {
        if (response && response.success) {
          this._log('图片打印请求成功');
          return response;
        }
        
        throw new Error(response?.error || '打印失败: 未知错误');
      }).catch(error => {
        this._log('发送图片打印请求失败:', error);
        
        // 异常情况下，尝试降级
        if (this.fallbackHandler) {
          this._log('使用降级打印方式');
          return this.fallbackHandler(base64Data, printerName, options, 'image');
        }
        
        throw error;
      });
    } catch (error) {
      this._log('打印图片异常:', error);
      
      // 异常情况下，尝试降级
      if (this.fallbackHandler) {
        this._log('图片打印异常，尝试降级处理');
        return this.fallbackHandler(base64Data, printerName, options, 'image');
      }
      
      return Promise.reject(error);
    }
  }

  /**
   * 保存PDF文件
   * @param {Blob} pdfBlob - PDF文件Blob
   * @param {string} fileName - 文件名
   * @returns {Promise<Object>} 保存结果
   */
  async savePdf(pdfBlob, fileName = 'document.pdf') {
    try {
      // 如果没有连接，先连接
      if (!this.connected) {
        await this.connect();
      }

      // 将PDF转换为Base64
      const base64Data = await this._blobToBase64(pdfBlob);

      // 发送保存请求
      const response = await this.client.send({
        action: 'savePdf',
        data: base64Data,
        options: {
          defaultPath: fileName
        }
      });

      if (response && response.success) {
        return response;
      }

      throw new Error(response.error || '保存失败: 未知错误');
    } catch (error) {
      this._log('保存PDF失败:', error);

      // 如果是连接错误，尝试使用浏览器下载
      try {
        const url = URL.createObjectURL(pdfBlob);
        const a = document.createElement('a');
        a.href = url;
        a.download = fileName;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
        return { success: true, fallback: true, message: '使用浏览器下载功能保存文件' };
      } catch (fallbackError) {
        this._log('降级保存失败:', fallbackError);
        throw error;
      }
    }
  }

  /**
   * 设置默认打印机
   * @param {string} printerName - 打印机名称
   */
  setDefaultPrinter(printerName) {
    this.options.defaultPrinter = printerName;
    this._saveDefaultPrinter();
    this._triggerEvent('printerChange', { printer: printerName });
  }

  /**
   * 设置降级处理函数，当打印服务不可用时使用
   * @param {Function} handler - 降级处理函数
   */
  setFallbackHandler(handler) {
    if (typeof handler !== 'function') {
      throw new Error('降级处理函数必须是一个函数');
    }
    
    // 包装降级处理函数，支持PDF和图片打印
    this.fallbackHandler = (data, printerName, options = {}, type = 'pdf') => {
      this._log('使用降级处理函数', { type, printer: printerName });
      
      try {
        if (type === 'pdf') {
          // PDF打印降级处理
          return handler(data, printerName, options, type);
        } else if (type === 'image') {
          // 图片打印降级处理
          this._log('使用降级方式打印图片');
          
          // 图片数据处理
          let imageUrl = data;
          if (data.startsWith('data:image')) {
            // 已经是Data URL格式
            imageUrl = data;
          } else if (/^[A-Za-z0-9+/=]+$/.test(data.replace(/\s/g, ''))) {
            // 纯base64数据，添加前缀
            imageUrl = `data:image/png;base64,${data}`;
          }
          
          // 创建HTML文档
          const html = `
            <!DOCTYPE html>
            <html>
              <head>
                <title>打印图片</title>
                <style>
                  body {
                    margin: 0;
                    padding: 0;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    height: 100vh;
                  }
                  img {
                    max-width: 100%;
                    max-height: 100%;
                  }
                  @media print {
                    body {
                      height: auto;
                    }
                    @page {
                      margin: 0;
                      size: auto;
                    }
                  }
                </style>
              </head>
              <body>
                <img src="${imageUrl}" alt="打印图片" />
                <script>
                  window.onload = function() {
                    setTimeout(function() {
                      window.print();
                      setTimeout(function() {
                        window.close();
                      }, 500);
                    }, 500);
                  };
                </script>
              </body>
            </html>
          `;
          
          // 创建Blob并打开新窗口
          const blob = new Blob([html], { type: 'text/html' });
          const url = URL.createObjectURL(blob);
          
          // 打开打印窗口
          const printWindow = window.open(url);
          if (!printWindow) {
            throw new Error('无法打开打印窗口，请检查浏览器是否阻止了弹出窗口');
          }
          
          // 检查窗口是否关闭以释放URL
          const checkClosed = setInterval(() => {
            if (printWindow.closed) {
              clearInterval(checkClosed);
              URL.revokeObjectURL(url);
              this._log('打印窗口已关闭，释放资源');
            }
          }, 1000);
          
          return Promise.resolve({
            success: true,
            message: '图片已通过浏览器打印功能发送到打印机'
          });
        } else {
          throw new Error(`不支持的打印类型: ${type}`);
        }
      } catch (error) {
        this._log('降级处理失败:', error);
        return Promise.reject(error);
      }
    };
    
    this._log('已设置降级处理函数');
    return this;
  }

  /**
   * 设置调试模式
   * @param {boolean} debug - 是否启用调试
   */
  setDebug(debug) {
    this.options.debug = debug;
    this.client.debug = debug;
  }

  /**
   * 设置服务器地址
   * @param {string} url - 服务器地址
   */
  setServerUrl(url) {
    this.options.serverUrl = url;
    
    // 如果已连接，需要重新连接
    if (this.connected) {
      this.disconnect();
    }
    
    this.client = new WebSocketClient(url);
    this.client.debug = this.options.debug;
    
    // 重新设置回调
    this.client.on('onConnect', () => this._onConnect());
    this.client.on('onDisconnect', () => this._onDisconnect());
    this.client.on('onError', (error) => this._onError(error));
    
    // 自动重连
    if (this.options.autoConnect) {
      this.connect();
    }
  }

  /**
   * 连接成功回调
   * @private
   */
  _onConnect() {
    this.connected = true;
    this._triggerEvent('printerServiceConnected');
    this._log('已连接到打印服务');
    
    // 连接成功后获取打印机列表
    this.getPrinters().catch(error => {
      this._log('自动获取打印机列表失败:', error);
    });
  }

  /**
   * 连接断开回调
   * @private
   */
  _onDisconnect() {
    this.connected = false;
    this._triggerEvent('printerServiceDisconnected');
    this._log('已断开与打印服务的连接');
  }

  /**
   * 错误回调
   * @private
   * @param {Error} error - 错误信息
   */
  _onError(error) {
    this._triggerEvent('printerServiceError', { error });
    this._log('打印服务错误:', error);
  }

  /**
   * 将Blob对象转换为Base64字符串
   * @private
   * @param {Blob} blob - 要转换的Blob对象
   * @returns {Promise<string>} Promise，解析为Base64字符串
   */
  _blobToBase64(blob) {
    return new Promise((resolve, reject) => {
      try {
        // 确保输入是Blob对象
        if (!(blob instanceof Blob)) {
          reject(new Error('输入必须是Blob对象'));
          return;
        }
        
        // 检查blob大小
        if (blob.size === 0) {
          reject(new Error('Blob大小为0'));
          return;
        }

        // 验证并修正MIME类型
        let pdfBlob = blob;
        if (blob.type !== 'application/pdf') {
          this._log(`修正PDF的MIME类型 (${blob.type} -> application/pdf)`);
          pdfBlob = new Blob([blob], { type: 'application/pdf' });
        }

        // 直接将Blob转换为字符串
        const reader = new FileReader();
        
        reader.onload = () => {
          try {
            if (!reader.result) {
              reject(new Error('FileReader 结果为空'));
              return;
            }
            
            if (typeof reader.result === 'string') {
              // 从 data:application/pdf;base64,XXXX 中提取 base64 部分
              const base64String = reader.result.split(',')[1];
              if (!base64String) {
                reject(new Error('无法从 FileReader 结果中提取 base64 数据'));
                return;
              }
              
              // 验证base64数据长度
              if (base64String.length === 0) {
                reject(new Error('提取的base64数据为空'));
                return;
              }
              
              this._log('成功转换PDF为base64，大小:', Math.round(base64String.length / 1024), 'KB');
              resolve(base64String);
            } else if (reader.result instanceof ArrayBuffer) {
              // 尝试处理ArrayBuffer结果
              const uint8Array = new Uint8Array(reader.result);
              const binaryString = Array.from(uint8Array)
                .map(byte => String.fromCharCode(byte))
                .join('');
              const base64 = btoa(binaryString);
              
              this._log('通过ArrayBuffer转换为base64，大小:', Math.round(base64.length / 1024), 'KB');
              resolve(base64);
            } else {
              reject(new Error(`FileReader 结果类型不支持: ${typeof reader.result}`));
            }
          } catch (error) {
            this._log('处理 FileReader 结果失败:', error);
            reject(error);
          }
        };
        
        reader.onerror = () => {
          this._log('读取 Blob 数据失败:', reader.error);
          reject(new Error(`读取 Blob 数据失败: ${reader.error?.message || '未知错误'}`));
        };
        
        // 使用readAsDataURL是最可靠的转换方式
        reader.readAsDataURL(pdfBlob);
      } catch (error) {
        this._log('_blobToBase64 处理异常:', error);
        reject(error);
      }
    });
  }

  /**
   * 从localStorage加载默认打印机
   * @private
   */
  _loadDefaultPrinter() {
    try {
      const printer = localStorage.getItem('defaultPrinter');
      if (printer) {
        this.options.defaultPrinter = printer;
      }
    } catch (error) {
      this._log('加载默认打印机失败:', error);
    }
  }

  /**
   * 保存默认打印机到localStorage
   * @private
   */
  _saveDefaultPrinter() {
    try {
      if (this.options.defaultPrinter) {
        localStorage.setItem('defaultPrinter', this.options.defaultPrinter);
      }
    } catch (error) {
      this._log('保存默认打印机失败:', error);
    }
  }

  /**
   * 触发自定义事件
   * @private
   * @param {string} eventName - 事件名称
   * @param {Object} detail - 事件详情
   */
  _triggerEvent(eventName, detail = {}) {
    if (typeof window !== 'undefined') {
      const event = new CustomEvent(eventName, { detail });
      window.dispatchEvent(event);
    }
  }

  /**
   * 打印日志
   * @private
   * @param  {...any} args - 日志参数
   */
  _log(...args) {
    if (this.options.debug) {
      console.log('[WMSPrinter]', ...args);
    }
  }

  /**
   * 设置自定义端口
   * @param {number} port - 端口号 
   * @param {string} host - 主机地址，默认为localhost
   * @returns {Object} 当前实例，用于链式调用
   */
  setCustomPort(port, host = 'localhost') {
    if (!port || isNaN(port) || port < 1 || port > 65535) {
      this._log('无效的端口号:', port);
      throw new Error('无效的端口号，端口必须是1-65535之间的数字');
    }
    
    const newUrl = `ws://${host}:${port}`;
    this._log(`设置自定义连接地址: ${newUrl}`);
    
    // 保存到本地存储，以便下次使用
    try {
      localStorage.setItem('wmsPrinterUrl', newUrl);
      this._log('已保存连接地址到本地存储');
    } catch (error) {
      this._log('保存连接地址失败:', error);
    }
    
    // 设置新的服务器地址
    this.setServerUrl(newUrl);
    
    return this;
  }
}

// 创建单例实例
const wmsPrinter = new WMSPrinter();

/**
 * WMS打印SDK使用说明：
 * 
 * 此SDK提供了用于与WMS打印服务进行通信的功能。
 * 它使用范围限定的XMLHttpRequest拦截，只影响打印服务相关的请求，
 * 不会影响应用程序的其他部分对XMLHttpRequest的使用。
 * 
 * 主要功能：
 * 1. 与打印服务建立WebSocket连接
 * 2. 获取可用打印机列表
 * 3. 打印PDF文档和图片
 * 4. 保存PDF文件
 * 5. 设置默认打印机
 * 
 * 使用示例：
 * import wmsPrinter from './wms-printer.js';
 * 
 * // 连接到打印服务
 * wmsPrinter.connect()
 *   .then(() => console.log('已连接'))
 *   .catch(err => console.error('连接失败', err));
 * 
 * // 获取打印机列表
 * wmsPrinter.getPrinters()
 *   .then(printers => console.log('可用打印机:', printers))
 *   .catch(err => console.error('获取打印机失败', err));
 */

// 导出单例
export default wmsPrinter; 